/*!	 filesystemnative.cpp
**	 FileSystemNative
**
**	......... ... 2013 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <giomm.h>
#include <glibmm.h>
#include <sys/stat.h>

#include "general.h"
#include <synfig/localization.h>

#include "filesystemnative.h"
#include "guid.h"

#endif

using namespace std;
using namespace etl;
using namespace synfig;

const FileSystemNative::Handle FileSystemNative::instance__(new FileSystemNative);

// ReadStream

FileSystemNative::ReadStream::ReadStream(FileSystem::Handle file_system, FILE *file):
    FileSystem::ReadStream(file_system), file_(file) { }

FileSystemNative::ReadStream::~ReadStream()
{
    fclose(file_);
}

size_t FileSystemNative::ReadStream::internal_read(void *buffer, size_t size)
{
    return fread(buffer, 1, size, file_);
}

// WriteStream

FileSystemNative::WriteStream::WriteStream(FileSystem::Handle file_system, FILE *file):
    FileSystem::WriteStream(file_system), file_(file) { }

FileSystemNative::WriteStream::~WriteStream()
{
    fclose(file_);
}

size_t FileSystemNative::WriteStream::internal_write(const void *buffer, size_t size)
{
    return fwrite(buffer, 1, size, file_);
}

// FileSystemNative

FileSystemNative::FileSystemNative() { }
FileSystemNative::~FileSystemNative() { }

bool FileSystemNative::is_file(const String &filename)
{
    return Gio::File::create_for_path(fix_slashes(filename))->query_file_type()
           == Gio::FILE_TYPE_REGULAR;
}

bool FileSystemNative::is_directory(const String &filename)
{
    return Gio::File::create_for_path(fix_slashes(filename))->query_file_type()
           == Gio::FILE_TYPE_DIRECTORY;
}

bool FileSystemNative::directory_create(const String &dirname)
{
    return is_directory(dirname)
           || Gio::File::create_for_path(fix_slashes(dirname))->make_directory();
}

bool FileSystemNative::directory_scan(const String &dirname, FileList &out_files)
{
    out_files.clear();

    if (!is_directory(dirname)) {
        return false;
    }

    Glib::Dir dir(dirname);

    for (Glib::DirIterator i = dir.begin(); i != dir.end(); ++i) {
        out_files.push_back(Glib::filename_to_utf8(*i));
    }

    return true;
}

bool FileSystemNative::file_remove(const String &filename)
{
    return 0 == remove(fix_slashes(filename).c_str());
}

bool FileSystemNative::file_rename(const String &from_filename, const String &to_filename)
{
#ifdef _WIN32

    // On Win32 platforms, rename() has bad behavior. Work around it.

    // Make random filename and ensure there's no file with such name exist
    struct stat buf;
    String old_file;

    do {
        synfig::GUID guid;
        old_file = to_filename + "." + guid.get_string().substr(0, 8);
    } while (stat(Glib::locale_from_utf8(old_file).c_str(), &buf) != -1);

    rename(Glib::locale_from_utf8(to_filename).c_str(), Glib::locale_from_utf8(old_file).c_str());

    if (rename(Glib::locale_from_utf8(from_filename).c_str(), Glib::locale_from_utf8(to_filename).c_str()) != 0) {
        rename(Glib::locale_from_utf8(old_file).c_str(), Glib::locale_from_utf8(to_filename).c_str());
        synfig::error("synfig::save_canvas(): Unable to rename file to correct filename, errno=%d", errno);
        return false;
    }

    remove(Glib::locale_from_utf8(old_file).c_str());

    return true;
#else
    return 0 == rename(from_filename.c_str(), to_filename.c_str());
#endif
}

FileSystem::ReadStream::Handle FileSystemNative::get_read_stream(const String &filename)
{
#ifdef _WIN32
    FILE *f = fopen(Glib::locale_from_utf8(fix_slashes(filename)).c_str(), "rb");
#else
    FILE *f = fopen(fix_slashes(filename).c_str(), "rb");
#endif
    return f == NULL
           ? FileSystem::ReadStream::Handle()
           : FileSystem::ReadStream::Handle(new ReadStream(this, f));
}

FileSystem::WriteStream::Handle FileSystemNative::get_write_stream(const String &filename)
{
#ifdef _WIN32
    FILE *f = fopen(Glib::locale_from_utf8(fix_slashes(filename)).c_str(), "wb");
#else
    FILE *f = fopen(fix_slashes(filename).c_str(), "wb");
#endif
    return f == NULL
           ? FileSystem::WriteStream::Handle()
           : FileSystem::WriteStream::Handle(new WriteStream(this, f));
}

String FileSystemNative::get_real_uri(const String &filename)
{
    if (filename.empty()) {
        return String();
    }

    return Glib::filename_to_uri(etl::absolute_path(filename));
}